package com.mini.grpc.examples.helloworld;

import com.mini.grpc.client.ManagedChannel;
import com.mini.grpc.client.ManagedChannelBuilder;
import com.mini.grpc.common.StatusRuntimeException;
import com.mini.grpc.common.StreamObserver;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import java.util.Arrays;
import java.util.List;

/**
 * HelloWorld gRPC 客户端
 * 演示各种类型的 gRPC 调用：一元调用、服务端流、客户端流、双向流
 */
public class HelloWorldClient {
    
    private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());
    
    private final HelloWorldServiceGrpc.HelloWorldServiceBlockingStub blockingStub;
    private final HelloWorldServiceGrpc.HelloWorldServiceStub asyncStub;
    private final ManagedChannel channel;
    
    /**
     * 创建客户端实例
     * 
     * @param channel gRPC 通道
     */
    public HelloWorldClient(ManagedChannel channel) {
        this.channel = channel;
        blockingStub = HelloWorldServiceGrpc.newBlockingStub(channel);
        asyncStub = HelloWorldServiceGrpc.newStub(channel);
    }
    
    /**
     * 演示一元调用
     * 
     * @param name 要问候的名字
     */
    public void demonstrateUnaryCall(String name) {
        logger.info("=== 演示一元调用 ===");
        logger.info("发送请求: " + name);
        
        try {
            long startTime = System.currentTimeMillis();
            String response = blockingStub.sayHello(name);
            long endTime = System.currentTimeMillis();
            
            logger.info("收到响应: " + response);
            logger.info("调用耗时: " + (endTime - startTime) + "ms");
            
        } catch (StatusRuntimeException e) {
            logger.severe("一元调用失败: " + e.getStatus());
            e.printStackTrace();
        }
    }
    
    /**
     * 演示服务端流式调用
     * 
     * @param name 要问候的名字
     */
    public void demonstrateServerStreamingCall(String name) {
        logger.info("=== 演示服务端流式调用 ===");
        logger.info("发送请求: " + name);
        
        try {
            CountDownLatch finishLatch = new CountDownLatch(1);
            AtomicInteger messageCount = new AtomicInteger(0);
            long startTime = System.currentTimeMillis();
            
            blockingStub.sayHelloServerStream(name, new StreamObserver<String>() {
                @Override
                public void onNext(String response) {
                    int count = messageCount.incrementAndGet();
                    logger.info(String.format("收到流消息 #%d: %s", count, response));
                }
                
                @Override
                public void onError(Throwable t) {
                    logger.severe("服务端流调用发生错误: " + t.getMessage());
                    t.printStackTrace();
                    finishLatch.countDown();
                }
                
                @Override
                public void onCompleted() {
                    long endTime = System.currentTimeMillis();
                    logger.info(String.format("服务端流调用完成，共收到 %d 条消息，耗时: %dms", 
                        messageCount.get(), endTime - startTime));
                    finishLatch.countDown();
                }
            });
            
            // 等待流完成
            if (!finishLatch.await(30, TimeUnit.SECONDS)) {
                logger.warning("服务端流调用超时");
            }
            
        } catch (Exception e) {
            logger.severe("服务端流调用失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * 演示客户端流式调用
     * 
     * @param names 要发送的名字列表
     */
    public void demonstrateClientStreamingCall(List<String> names) {
        logger.info("=== 演示客户端流式调用 ===");
        logger.info("准备发送 " + names.size() + " 个名字: " + names);
        
        try {
            CountDownLatch finishLatch = new CountDownLatch(1);
            long startTime = System.currentTimeMillis();
            
            StreamObserver<String> requestObserver = asyncStub.sayHelloClientStream(
                new StreamObserver<String>() {
                    @Override
                    public void onNext(String response) {
                        long endTime = System.currentTimeMillis();
                        logger.info("收到最终响应: " + response);
                        logger.info("客户端流调用耗时: " + (endTime - startTime) + "ms");
                    }
                    
                    @Override
                    public void onError(Throwable t) {
                        logger.severe("客户端流调用发生错误: " + t.getMessage());
                        t.printStackTrace();
                        finishLatch.countDown();
                    }
                    
                    @Override
                    public void onCompleted() {
                        logger.info("客户端流调用完成");
                        finishLatch.countDown();
                    }
                }
            );
            
            // 发送所有名字
            for (int i = 0; i < names.size(); i++) {
                String name = names.get(i);
                logger.info(String.format("发送客户端流消息 #%d: %s", i + 1, name));
                requestObserver.onNext(name);
                
                // 模拟发送间隔
                Thread.sleep(200);
            }
            
            // 完成发送
            requestObserver.onCompleted();
            logger.info("客户端流发送完成，等待服务端响应...");
            
            // 等待响应
            if (!finishLatch.await(30, TimeUnit.SECONDS)) {
                logger.warning("客户端流调用超时");
            }
            
        } catch (Exception e) {
            logger.severe("客户端流调用失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * 演示双向流式调用
     * 
     * @param names 要发送的名字列表
     */
    public void demonstrateBidirectionalStreamingCall(List<String> names) {
        logger.info("=== 演示双向流式调用 ===");
        logger.info("准备进行双向流通信，发送 " + names.size() + " 个名字: " + names);
        
        try {
            CountDownLatch finishLatch = new CountDownLatch(1);
            AtomicInteger responseCount = new AtomicInteger(0);
            long startTime = System.currentTimeMillis();
            
            StreamObserver<String> requestObserver = asyncStub.sayHelloBidiStream(
                new StreamObserver<String>() {
                    @Override
                    public void onNext(String response) {
                        int count = responseCount.incrementAndGet();
                        logger.info(String.format("收到双向流响应 #%d: %s", count, response));
                    }
                    
                    @Override
                    public void onError(Throwable t) {
                        logger.severe("双向流调用发生错误: " + t.getMessage());
                        t.printStackTrace();
                        finishLatch.countDown();
                    }
                    
                    @Override
                    public void onCompleted() {
                        long endTime = System.currentTimeMillis();
                        logger.info(String.format("双向流调用完成，共收到 %d 条响应，耗时: %dms", 
                            responseCount.get(), endTime - startTime));
                        finishLatch.countDown();
                    }
                }
            );
            
            // 发送所有名字
            for (int i = 0; i < names.size(); i++) {
                String name = names.get(i);
                logger.info(String.format("发送双向流消息 #%d: %s", i + 1, name));
                requestObserver.onNext(name);
                
                // 模拟发送间隔
                Thread.sleep(300);
            }
            
            // 完成发送
            requestObserver.onCompleted();
            logger.info("双向流发送完成，等待服务端完成响应...");
            
            // 等待完成
            if (!finishLatch.await(30, TimeUnit.SECONDS)) {
                logger.warning("双向流调用超时");
            }
            
        } catch (Exception e) {
            logger.severe("双向流调用失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * 关闭客户端
     */
    public void shutdown() throws InterruptedException {
        logger.info("正在关闭客户端...");
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
        logger.info("客户端已关闭");
    }
    
    /**
     * 主方法 - 演示所有类型的调用
     * 
     * @param args 命令行参数，可选的服务端地址
     */
    public static void main(String[] args) throws Exception {
        String target = "localhost:50051";
        
        // 解析命令行参数
        if (args.length > 0) {
            target = args[0];
        }
        
        logger.info("连接到 gRPC 服务端: " + target);
        
        // 创建通道
        ManagedChannel channel = ManagedChannelBuilder.forTarget(target)
            .usePlaintext()
            .build();
        
        try {
            HelloWorldClient client = new HelloWorldClient(channel);
            
            logger.info("开始演示 Mini-gRPC 各种调用类型...\n");
            
            // 1. 演示一元调用
            client.demonstrateUnaryCall("Alice");
            Thread.sleep(1000);
            
            client.demonstrateUnaryCall("Bob");
            Thread.sleep(2000);
            
            // 2. 演示服务端流式调用
            client.demonstrateServerStreamingCall("Charlie");
            Thread.sleep(2000);
            
            // 3. 演示客户端流式调用
            List<String> clientStreamNames = Arrays.asList("David", "Eve", "Frank", "Grace");
            client.demonstrateClientStreamingCall(clientStreamNames);
            Thread.sleep(2000);
            
            // 4. 演示双向流式调用
            List<String> bidiStreamNames = Arrays.asList("Henry", "Ivy", "Jack");
            client.demonstrateBidirectionalStreamingCall(bidiStreamNames);
            
            logger.info("\n=== 所有调用演示完成 ===");
            
        } catch (Exception e) {
            logger.severe("客户端运行时发生错误: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // 关闭通道
            channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
            logger.info("客户端已完全关闭");
        }
    }
} 